#include "pvm/pvmapi.h"
#include "lib/pvmsystem.h"
#include "lib/decoders/speex/pvmsourcefactoryspeex.h"
#include "lib/decoders/speex/pvmvoicesourcespeex.h"
#include "lib/decoders/speex/pvmvoicespeex.h"

#include "lib/memory/pvmmemory.h"

static const int BLOCK_SIZE=256;

PVMSourceFatcorySpeex::PVMSourceFatcorySpeex()
{
}

PVMSourceFatcorySpeex::~PVMSourceFatcorySpeex()
{
}

pvm_bool PVMSourceFatcorySpeex::CanLoad(IPvmInStream* inStream, pvm_int8** /*startData*/, pvm_int* /*startDataSize*/)
{
	if(!inStream->Seekable())
		return false;

	inStream->Seek(0, EPVM_SEEK_BEGIN);

	SpeexBits bits;
	ogg_int64_t page_granule;
	ogg_int64_t last_granule;

	int skip_samples;
	int page_nb_packets;
	int speex_serialno;
	spx_int32_t frame_size;
	int granule_frame_size;
	int nframes;
	int extra_headers;
	int packet_count;
	spx_int32_t enh_enabled;
	spx_int32_t rate;
	int forceMode;
	int channels;
	void* st;
	int lookahead;
	int packet_no;
	pvm_bool stream_init;

	ogg_sync_state oy;
	ogg_page       og;
	ogg_packet     op;
	ogg_stream_state os;
	SpeexStereoState stereo;

	page_granule = 0;
	last_granule = 0;

	skip_samples = 0;
	page_nb_packets = 0;
	speex_serialno = -1;
	frame_size = 0;
	granule_frame_size = 0;
	nframes = 2;
	extra_headers = 0;
	packet_count = 0;
	enh_enabled = 0;
	rate = 0;
	forceMode = -1;
	channels = -1;
	st = NULL;
	lookahead = 0;
	packet_no = -1;
	stream_init = false;

	pvm_bool eos = false;
	ogg_sync_init(&oy);
	speex_bits_init(&bits);
	pvm_bool runFlag = true;
	pvm_bool isInitialized = false;
	while(runFlag)
	{
		char* data;
		int nb_read;
		data = ogg_sync_buffer(&oy, BLOCK_SIZE);
		//Read bitstream from input file
		nb_read = inStream->Read(data, BLOCK_SIZE);//fread(data, sizeof(char), 200, fin);      
		if(nb_read<=0)
			break;
		ogg_sync_wrote(&oy, nb_read);

		while(runFlag && ogg_sync_pageout(&oy, &og)==1)
		{
			if (!stream_init) {
				ogg_stream_init(&os, ogg_page_serialno(&og));
				stream_init = true;
			}
			if (ogg_page_serialno(&og) != os.serialno) {
				/* so all streams are read. */
				ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
			}
			/*Add page to the bitstream*/
			ogg_stream_pagein(&os, &og);
			page_granule = ogg_page_granulepos(&og);
			page_nb_packets = ogg_page_packets(&og);
			if (page_granule>0 && frame_size)
			{
				/* FIXME: shift the granule values if --force-* is specified */
				skip_samples = (int)(frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size);
				if (ogg_page_eos(&og))
					skip_samples = -skip_samples;
				/*else if (!ogg_page_bos(&og))
				skip_samples = 0;*/
			}
			else
			{
				skip_samples = 0;
			}
			last_granule = page_granule;
			packet_no=0;
			while (!eos && ogg_stream_packetout(&os, &op) == 1)
			{
				if (op.bytes>=5 && !memcmp(op.packet, "Speex", 5)) {
					speex_serialno = os.serialno;
				}
				if (speex_serialno == -1 || os.serialno != speex_serialno)
					break;

				st = PVMVoiceSpeex::process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &stereo, &extra_headers);
				if (st)
					isInitialized = true;

				runFlag = false;
				break;
			}
		}
	}

	inStream->Seek(0, EPVM_SEEK_BEGIN);

	if (st)
		speex_decoder_destroy(st);

	speex_bits_destroy(&bits);
	if (stream_init)
		ogg_stream_clear(&os);
	ogg_sync_clear(&oy);
	
	return isInitialized;
}

IPVMVoiceSource* PVMSourceFatcorySpeex::CreateVoiceSource(IPvmInStream* inStream, pvm_int8* /*startData*/, pvm_int /*startDataSize*/)
{
	if(!inStream->Seekable())
		return NULL;
	return new PVMVoiceSourceSpeex(inStream);
}

IPVMSourceFatcory* PVMSourceFatcorySpeex::CreateFactory()
{
	return new PVMSourceFatcorySpeex();
}

